using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace UnityEngine.SocialPlatforms.GameCenter
{
#if ENABLE_GAMECENTER
	using UnityEngine.SocialPlatforms.Impl;
	
	[StructLayout (LayoutKind.Sequential)]
	internal struct GcUserProfileData
	{
		public string userName;
		public string userID;
		public int isFriend;
		public Texture2D image;
		
		public UserProfile ToUserProfile()
		{
			return new UserProfile(userName, userID, (isFriend==1?true:false), UserState.Offline, image);
		}
		
		public void AddToArray(ref UserProfile[] array, int number)
		{
			if (array.Length > number && number >= 0)
				array[number] = ToUserProfile();
			else
				Debug.Log("Index number out of bounds when setting user data");
		}
	}

	[StructLayout (LayoutKind.Sequential)]
	internal struct GcAchievementDescriptionData
	{
		public string m_Identifier;
		public string m_Title;
		public Texture2D m_Image;
		public string m_AchievedDescription;
		public string m_UnachievedDescription;
		public int m_Hidden;
		public int m_Points;
		
		public AchievementDescription ToAchievementDescription()
		{
			return new AchievementDescription(
				m_Identifier, 
				m_Title, 
				m_Image, 
				m_AchievedDescription, 
				m_UnachievedDescription, 
				m_Hidden == 0 ? false : true,
				m_Points);
		}
	}

	[StructLayout (LayoutKind.Sequential)]
	internal struct GcAchievementData
	{
		public string m_Identifier;
		public double m_PercentCompleted;
		public int m_Completed;
		public int m_Hidden;
		public int m_LastReportedDate;
		
		public Achievement ToAchievement()
		{
			return new Achievement(
				m_Identifier, 
				m_PercentCompleted, 
				m_Completed == 0 ? false : true, 
				m_Hidden == 0 ? false : true, 
				new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(m_LastReportedDate));
		}
	}

	[StructLayout (LayoutKind.Sequential)]
	internal struct GcScoreData
	{
		public string m_Category;
		public int m_ValueLow;
		public int m_ValueHigh;
		public int m_Date;
		public string m_FormattedValue;
		public string m_PlayerID;
		public int m_Rank;
		
		public Score ToScore()
		{
			return new Score(
				m_Category, 
				(((Int64)m_ValueHigh) << 32) + m_ValueLow,
				m_PlayerID,
				new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(m_Date),
				m_FormattedValue, 
				m_Rank);
		}
	}
	
	public partial class GameCenterPlatform : ISocialPlatform
	{
		static Action<bool> s_AuthenticateCallback;
		static Action<bool> s_FriendsCallback;
		static Action<IAchievementDescription[]> s_AchievementDescriptionLoaderCallback;
		static Action<IAchievement[]> s_AchievementLoaderCallback;
		static Action<bool> s_ProgressCallback;
		static Action<bool> s_ScoreCallback;
		static Action<IScore[]> s_ScoreLoaderCallback;
		static Action<bool> s_LeaderboardCallback;
		static Action<IUserProfile[]> s_UsersCallback;
		static AchievementDescription[] s_adCache = new AchievementDescription[0];
		static UserProfile[] s_friends = new UserProfile[0];
		static UserProfile[] s_users = new UserProfile[0];
		static Action<bool> s_ResetAchievements;
		private static LocalUser m_LocalUser;
		private static List<GcLeaderboard> m_GcBoards = new List<GcLeaderboard>();
		
		static void ClearAchievementDescriptions(int size)
		{
			if (s_adCache == null || s_adCache.Length != size)
				s_adCache = new AchievementDescription[size];
		}
		
		static void SetAchievementDescription(GcAchievementDescriptionData data, int number)
		{
			s_adCache[number] = data.ToAchievementDescription();
		}
		
		static void SetAchievementDescriptionImage(Texture2D texture, int number)
		{
			if (s_adCache.Length <= number || number < 0)
			{
				Debug.Log("Achievement description number out of bounds when setting image");
				return;
			}
			s_adCache[number].SetImage(texture);
		}
		
		static void TriggerAchievementDescriptionCallback()
		{
			if (s_AchievementDescriptionLoaderCallback != null && s_adCache != null)
			{
				if (s_adCache.Length == 0)
					Debug.Log("No achivevement descriptions returned");
				s_AchievementDescriptionLoaderCallback(s_adCache);
			}
		}
		
		static void AuthenticateCallbackWrapper(int result)
		{
			if (s_AuthenticateCallback != null)
			{
				PopulateLocalUser();
				s_AuthenticateCallback((result == 1 ? true : false));
			}
		}
		
		static void ClearFriends(int size)
		{
			SafeClearArray(ref s_friends, size);
		}
		
		static void SetFriends(GcUserProfileData data, int number)
		{
			data.AddToArray(ref s_friends, number);
		}
		
		static void SetFriendImage(Texture2D texture, int number)
		{
			SafeSetUserImage(ref s_friends, texture, number);
		}
		
		static void TriggerFriendsCallbackWrapper(int result)
		{
			if (s_friends != null)
				m_LocalUser.SetFriends(s_friends);
			if (s_FriendsCallback != null)
				s_FriendsCallback((result == 1 ? true : false));
		}
		
		static void AchievementCallbackWrapper(GcAchievementData[] result)
		{
			if (s_AchievementLoaderCallback != null)
			{
				if (result.Length == 0)
					Debug.Log("No achivevements returned");
				Achievement[] migrated = new Achievement[result.Length];
				for (int i = 0; i < result.Length; ++i)
					migrated[i] = result[i].ToAchievement();
				s_AchievementLoaderCallback(migrated);
			}
		}
		
		static void ProgressCallbackWrapper(bool success)
		{
			if (s_ProgressCallback != null)
				s_ProgressCallback(success);
		}
		
		static void ScoreCallbackWrapper(bool success)
		{
			if (s_ScoreCallback != null)
				s_ScoreCallback(success);
		}
		
		static void ScoreLoaderCallbackWrapper(GcScoreData[] result)
		{
			if (s_ScoreLoaderCallback != null)
			{
				Score[] migrated = new Score[result.Length];
				for (int i = 0; i < result.Length; ++i)
					migrated[i] = result[i].ToScore();
				s_ScoreLoaderCallback(migrated);
			}
		}
		
		void ISocialPlatform.LoadFriends(ILocalUser user, Action<bool> callback)
		{
			s_FriendsCallback = callback;
			Internal_LoadFriends();
		}
					
		void ISocialPlatform.Authenticate(ILocalUser user, Action<bool> callback)
		{
			s_AuthenticateCallback = callback;
			Internal_Authenticate();
		}
		
		public ILocalUser localUser
		{
			get
			{
				if (m_LocalUser == null)
					m_LocalUser = new LocalUser();
				
				if (Internal_Authenticated() && m_LocalUser.id == "0")
					PopulateLocalUser();
				return m_LocalUser;
			}
		}
		
		private static void PopulateLocalUser()
		{
			m_LocalUser.SetAuthenticated(Internal_Authenticated());
			m_LocalUser.SetUserName(Internal_UserName());
			m_LocalUser.SetUserID(Internal_UserID());
			m_LocalUser.SetUnderage(Internal_Underage());
			m_LocalUser.SetImage(Internal_UserImage());
		}
		
		public void LoadAchievementDescriptions(Action<IAchievementDescription[]> callback)
		{
			if (!VerifyAuthentication())
			{
				callback(new AchievementDescription[0]);
				return;
			}
			s_AchievementDescriptionLoaderCallback = callback;
			Internal_LoadAchievementDescriptions();
		}
		
		// TODO: This doesn't really work with the static callback wrapper, multiple progresses
		// could be reported at the same time.
		public void ReportProgress(string id, double progress, Action<bool> callback)
		{
			if (!VerifyAuthentication())
			{
				callback(false);
				return;
			}
			s_ProgressCallback = callback;
			Internal_ReportProgress(id, progress);
		}
		
		public void LoadAchievements(Action<IAchievement[]> callback)
		{
			if (!VerifyAuthentication())
			{
				callback(new Achievement[0]);
				return;
			}
			s_AchievementLoaderCallback = callback;
			Internal_LoadAchievements();
		}
		
		public void ReportScore(Int64 score, string board, Action<bool> callback)
		{
			if (!VerifyAuthentication())
			{
				callback(false);
				return;
			}
			s_ScoreCallback = callback;
			Internal_ReportScore(score, board);
		}
		
		public void LoadScores(string category, Action<IScore[]> callback)
		{
			if (!VerifyAuthentication())
			{
				callback(new Score[0]);
				return;
			}
			s_ScoreLoaderCallback = callback;
			Internal_LoadScores(category);
		}
		
		public void LoadScores(ILeaderboard board, Action<bool> callback)
		{
			if (!VerifyAuthentication())
			{
				callback(false);
				return;
			}
			s_LeaderboardCallback = callback;
			Leaderboard genericBoard = (Leaderboard)board;
			GcLeaderboard gcBoard = new GcLeaderboard(genericBoard);
			m_GcBoards.Add(gcBoard);
			if (genericBoard.GetUserFilter().Length > 0)
				gcBoard.Internal_LoadScoresWithUsers(board.id, (int)board.timeScope, genericBoard.GetUserFilter());
			else
				gcBoard.Internal_LoadScores(board.id, board.range.from, board.range.count, (int)board.userScope, (int)board.timeScope);
		}
		
		static void LeaderboardCallbackWrapper(bool success)
		{
			if (s_LeaderboardCallback != null)
				s_LeaderboardCallback(success);
		}
		
		public bool GetLoading(ILeaderboard board)
		{
			if (!VerifyAuthentication()) return false;
			foreach (GcLeaderboard gcBoard in m_GcBoards)
				if (gcBoard.Contains((Leaderboard)board))
					return gcBoard.Loading();
			return false;
		}
		
		private bool VerifyAuthentication()
		{
			if (!localUser.authenticated)
			{
				Debug.Log ("Must authenticate first");
				return false;
			}
			return true;
		}
		
		public void ShowAchievementsUI()
		{
			Internal_ShowAchievementsUI();
		}
		
		public void ShowLeaderboardUI()
		{
			Internal_ShowLeaderboardUI();
		}
		
		static void ClearUsers(int size)
		{
			SafeClearArray(ref s_users, size);
		}
		
		static void SetUser(GcUserProfileData data, int number)
		{
			data.AddToArray(ref s_users, number);
		}
		
		static void SetUserImage(Texture2D texture, int number)
		{
			SafeSetUserImage(ref s_users, texture, number);
		}
		
		static void TriggerUsersCallbackWrapper()
		{
			if (s_UsersCallback != null)
				s_UsersCallback(s_users);
		}
		
		public void LoadUsers(string[] userIds, Action<IUserProfile[]> callback)
		{
			if (!VerifyAuthentication())
			{
				callback(new UserProfile[0]);
				return;
			}
			s_UsersCallback = callback;
			Internal_LoadUsers(userIds);
		}
		
		private static void SafeSetUserImage(ref UserProfile[] array, Texture2D texture, int number)
		{
			if (array.Length <= number || number < 0)
			{
				Debug.Log("Invalid texture when setting user image");
				texture = new Texture2D(76, 76);
			}
			if (array.Length > number && number >= 0)
				array[number].SetImage(texture);
			else
				Debug.Log("User number out of bounds when setting image");
		}
		
		private static void SafeClearArray(ref UserProfile[] array, int size)
		{
			if (array == null || array.Length != size)
				array = new UserProfile[size];
		}
		
		public ILeaderboard CreateLeaderboard()
		{
			Leaderboard lb = new Leaderboard();
			return (ILeaderboard)lb;
		}
		
		public IAchievement CreateAchievement()
		{
			Achievement achoo = new Achievement();
			return (IAchievement)achoo;
		}
		
		static void TriggerResetAchievementCallback(bool result)
		{
			if (s_ResetAchievements != null)
				s_ResetAchievements(result);
		}
		

	}
#else
		public class GameCenterPlatform : UnityEngine.SocialPlatforms.Local
		{
			static public void ResetAllAchievements(Action<bool> callback)
			{
				Debug.Log ("ResetAllAchievements - no effect in editor");
				callback(true);
			}
		
			static public void ShowDefaultAchievementCompletionBanner(bool value)
			{
				Debug.Log ("ShowDefaultAchievementCompletionBanner - no effect in editor");
			}
		
			static public void ShowLeaderboardUI(string leaderboardID, TimeScope timeScope)
			{
				Debug.Log ("ShowLeaderboardUI - no effect in editor");
			}
		}
#endif
}

